package ldh.im.fxgui.controller.sub;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import ldh.im.fxbase.dao.ImMessageDao;
import ldh.im.fxbase.dao.ImSessionDao;
import ldh.im.fxbase.data.dto.SearchResult;
import ldh.im.fxbase.data.pojo.ImMessage;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.fxanimations.BounceInRightTransition;
import ldh.im.fxbase.fxanimations.BounceOutRightTransition;
import ldh.im.fxbase.util.BeanFactory;
import ldh.im.fxbase.util.FxThreadUtil;
import ldh.im.fxbase.util.ThreadPoolUtil;
import ldh.im.fxgui.component.cell.SearchMessageView;
import ldh.im.fxgui.component.cell.SearchResultView;
import ldh.im.fxgui.controller.MessageSessionController;
import ldh.im.fxgui.handle.IMessageShowHandler;
import ldh.im.fxgui.pojo.SearchMessage;
import ldh.im.fxgui.pojo.WrapSearchResult;
import ldh.im.fxgui.service.ContentSearchService;
import ldh.im.fxgui.util.UiUtil;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

public class SearchController implements Initializable, IMessageShowHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(SearchController.class);

    @FXML private TextField searchTextField;
    @FXML private Pane contentPane;
    @FXML private Pane sessionPane;
    @FXML private Pane showMessagePane;
    @FXML private Label searchTitle;
    @FXML private Button enterSessionBtn;

    private String search;

    private WrapSearchResult currentSearchResult = null;

    // 消息会话
    private ObservableList<WrapSearchResult> searchResultDatas = FXCollections.observableArrayList();
    private VirtualFlow<WrapSearchResult, ?> searchResultVirtualFlow;
    private VirtualizedScrollPane sessionScrollPane;

    // 搜索的消息展示
    private ObservableList<SearchMessage> messageDatas = FXCollections.observableArrayList();
    private VirtualFlow<SearchMessage, ?> messageVirtualFlow;
    private VirtualizedScrollPane messageScrollPane;

    // 查看指定消息
    private ObservableList<SearchMessage> showItems = FXCollections.observableArrayList();
    private VirtualFlow<SearchMessage, ?> showMessageList;

    private MessageSessionController messageSessionController;

    public void setMessageSearchResult(String search, List<WrapSearchResult> searchResultList) {
        this.search = search;

        searchTextField.setText(search);
        searchResultDatas.setAll(searchResultList);

        searchTextField.textProperty().addListener(l->{
            String searchStr = searchTextField.getText().trim();
            String userId = UiUtil.getLoginImUser().getUserId();
            search(userId, searchStr);
            LOGGER.info("search: {}", searchStr);
        });
    }

    public void setMessageSessionController(MessageSessionController messageSessionController) {
        this.messageSessionController = messageSessionController;
    }

    @Override
    public void showMessage(ImMessage message) {
        loadShowMessageData(message);
        BounceInRightTransition bounceInRightTransition = new BounceInRightTransition(showMessagePane);
        bounceInRightTransition.playFromStart();
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        enterSessionBtn.setVisible(false);
        searchResultVirtualFlow = VirtualFlow.createVertical(searchResultDatas, m->{
            SearchResultView searchResultView = new SearchResultView(m);
            searchResultView.addEventFilter(MouseEvent.MOUSE_PRESSED, e->{
                selectSession(m);
            });
            return Cell.wrapNode(searchResultView);
        });

        sessionScrollPane = new VirtualizedScrollPane(searchResultVirtualFlow);
        sessionPane.getChildren().setAll(sessionScrollPane);

        messageVirtualFlow = VirtualFlow.createVertical(messageDatas, m->{
            SearchMessageView searchResultView = new SearchMessageView(SearchController.this, m);
            return Cell.wrapNode(searchResultView);
        });

        messageScrollPane = new VirtualizedScrollPane(messageVirtualFlow);
        VBox.setVgrow(messageScrollPane, Priority.ALWAYS);
        contentPane.getChildren().add(messageScrollPane);

        initShowMessagePane();

        contentPane.widthProperty().addListener(l->{
            showMessagePane.setTranslateX(contentPane.getWidth());
        });
    }

    @FXML
    private void returnBtn(MouseEvent mouseEvent) {
        BounceOutRightTransition bounceOutRightTransition = new BounceOutRightTransition(showMessagePane);
        bounceOutRightTransition.setOnFinished(e->{
            showMessagePane.setTranslateX(contentPane.getWidth());
        });
        bounceOutRightTransition.playFromStart();
    }

    @FXML
    private void enterSession(ActionEvent actionEvent) {
        if (currentSearchResult == null) return;
        Task<ImSession> sessionTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ImSessionDao sessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);
                ImSession session = sessionDao.getSessionById(currentSearchResult.getSearchResult().getSessionId());
                return session;
            }
        };
        ThreadPoolUtil.submitTask(sessionTask);
        sessionTask.setOnSucceeded(s->{
            messageSessionController.switchSession(sessionTask.getValue());
        });
    }

    private void selectSession(WrapSearchResult wrapSearchResult) {
        currentSearchResult = wrapSearchResult;

        enterSessionBtn.setVisible(true);

        if (showMessagePane.getTranslateX() < 20) {
            returnBtn(null);
        }

        Task<List<ImMessage>> messageTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ImMessageDao messageDao = BeanFactory.getInstance().getBean(ImMessageDao.class);
                List<ImMessage> messages = messageDao.searchBySessionId(wrapSearchResult.getSearchResult().getSessionId(), search, null, 1000, false);
                return messages;
            }
        };
        ThreadPoolUtil.submitTask(messageTask, "搜索消息");
        messageTask.setOnSucceeded(s->{
            List<ImMessage> messages = messageTask.getValue();
            searchTitle.setText(String.format("%s条与'%s'记录相关", messages.size(), search));
            List<SearchMessage> searchMessages = messages.stream().map(m-> new SearchMessage(m, true)).collect(Collectors.toList());
            messageDatas.setAll(searchMessages);
        });
    }

    private void cleanSession() {
        searchTitle.setText("");
        currentSearchResult = null;

        messageDatas.clear();
        showItems.clear();
    }

    private void initShowMessagePane() {
        showMessageList = VirtualFlow.createVertical(showItems, m->{
            SearchMessageView searchMessageView = new SearchMessageView(SearchController.this, m);
            return Cell.wrapNode(searchMessageView);
        });
        VirtualizedScrollPane virtualizedScrollPane = new VirtualizedScrollPane<>(showMessageList);
        virtualizedScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        VBox.setVgrow(virtualizedScrollPane, Priority.ALWAYS);
        showMessagePane.getChildren().add(virtualizedScrollPane);
        showMessagePane.setTranslateX(contentPane.getWidth());
    }

    private void loadShowMessageData(ImMessage message) {
        ThreadPoolUtil.submit(()->{
            try {
                ImMessageDao imMessageDao = BeanFactory.getInstance().getBean(ImMessageDao.class);
                List<ImMessage> preMessages = imMessageDao.getMessagesBySessionId(message.getSessionId(), message.getServerReceiveTime(), null, 50, true);
                List<ImMessage> nextMessages =  imMessageDao.getMessagesBySessionId(message.getSessionId(), message.getServerReceiveTime(), null, 50, false);

                Set<ImMessage> messages = new HashSet<>(preMessages.size() + nextMessages.size() + 1);
                messages.addAll(preMessages);
                messages.add(message);
                messages.addAll(nextMessages);

                List<SearchMessage> wrapImMessages = sort(messages).stream().map(m->new SearchMessage(m, false)).collect(Collectors.toList());
                FxThreadUtil.runLater(()-> {
                    showItems.clear();
                    showItems.addAll(wrapImMessages);
                    int idx = showItems.indexOf(new SearchMessage(message, false));
                    showMessageList.show(idx);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void search(String userId, String search) {
        if (search.equals("")) {
            cleanSession();
            return;
        }
        Task<List<SearchResult>> searchResultsTask = new Task() {

            @Override
            protected Object call() throws Exception {
                ContentSearchService contentSearchService = BeanFactory.getInstance().getBean(ContentSearchService.class);
                List<SearchResult> messageSearchResult = contentSearchService.searchMessages(userId, search);
                return messageSearchResult;
            }
        };
        ThreadPoolUtil.submitTask(searchResultsTask);
        searchResultsTask.setOnSucceeded(s->{
            List<SearchResult> searchResults = searchResultsTask.getValue();
            List<WrapSearchResult> wrapSearchResults = searchResults.stream().map(sr->new WrapSearchResult(search, sr)).collect(Collectors.toList());
            searchResultDatas.setAll(wrapSearchResults);

            cleanSession();
        });
    }

    private List<ImMessage> sort(Collection<ImMessage> messages) {
       return messages.stream().sorted((m1, m2)->{
            if (m1.getServerReceiveTime() == null && m2.getServerReceiveTime() == null) return 0;
            if (m1.getServerReceiveTime() == null) return -1;
            if (m2.getServerReceiveTime() == null) return 1;
            return m1.getServerReceiveTime().compareTo(m2.getServerReceiveTime());
        }).collect(Collectors.toList());
    }
}
